#include <linux/myhead_for_driver.h>
#include "led_driver.h"
// LED1: PE10  start:0x50006000 <---> RCC (start)0x50000000 + (offset)A28(总线:AHB4)
// LED2: PF10  start:0x50007000 <---> RCC (start)0x50000000 + (offset)A28(总线:AHB4)
// LED3: PE8   start:0x50006000 <---> RCC (start)0x50000000 + (offset)A28(总线:AHB4)
// LED4: PZ5
// LED5: PZ6
// LED6: PZ7
#define DEV_NAME "led_driver"
#define CDEV_CLS_NAME "led_driver_info"
#define DEV_COUNT 6
#define DEV_MINOR_START_NUM 0

unsigned int major = 0;
unsigned int minor = 0;

struct cdev *led_cdev;
struct class *led_cls;
struct device *led_device;

struct device_node *led_node;
int gpio_num[6] = {0};

int led_init(void) // 形参void必须加
{
    int i;
    int err = 0;
    int res = 0;
    led_node = of_find_node_by_path("/myleds");
    if (NULL == led_node)
    {
        err = -ENODATA;
        goto led_init_ERR1;
    }

    gpio_num[0] = of_get_named_gpio(led_node, "myled1", 0);
    gpio_num[1] = of_get_named_gpio(led_node, "myled2", 0);
    gpio_num[2] = of_get_named_gpio(led_node, "myled3", 0);
    gpio_num[3] = of_get_named_gpio(led_node, "myled4", 0);
    gpio_num[4] = of_get_named_gpio(led_node, "myled5", 0);
    gpio_num[5] = of_get_named_gpio(led_node, "myled6", 0);

    for (i = 0; i < DEV_COUNT; i++)
    {
        res = gpio_request(gpio_num[i], NULL);
        if (res)
        {
            err = res;
            goto led_init_ERR2;
        }
    }

    for (i = 0; i < DEV_COUNT; i++)
    {
        res = gpio_direction_output(gpio_num[i], 0);
        if (res)
        {
            err = res;
            goto led_init_ERR3;
        }
    }

    return 0;

led_init_ERR3:
    i = 6;
led_init_ERR2:
    for (i--; i >= 0; i--)
    {
        gpio_free(gpio_num[i]);
    }
led_init_ERR1:
    return err;
}

int led_open(struct inode *inode, struct file *file)
{
    // printk("led_open\n");
    return 0;
}

ssize_t led_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
    return 0;
}

ssize_t led_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
    return 0;
}

long led_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
    int led_stat;
    int func = GET_FUNC_FROM_CMD(cmd) - 1;                           // 对应的数组下标
    copy_from_user(&led_stat, (void *)args, GET_SIZE_FROM_CMD(cmd));
    if (led_stat)
    {
        gpio_set_value(gpio_num[func], 1);
    }
    else
    {
        gpio_set_value(gpio_num[func], 0);
    }

    return 0;
}

int led_close(struct inode *inode, struct file *file)
{
    return 0;
}

static const struct file_operations led_fops = {
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .unlocked_ioctl = led_ioctl,
    .release = led_close};

static int __init led_driver_init(void)
{
    int err = 0;
    dev_t led_dev_num = 0;
    int res = 0;
    int i;

    led_cdev = cdev_alloc();

    if (NULL == led_cdev)
    {
        err = -ENOMEM;
        goto led_driver_init_ERR1;
    }

    cdev_init(led_cdev, &led_fops);

    if ((res = alloc_chrdev_region(&led_dev_num, DEV_MINOR_START_NUM, DEV_COUNT, DEV_NAME)))
    {
        err = res;
        goto led_driver_init_ERR2;
    }
    major = MAJOR(led_dev_num);
    minor = MINOR(led_dev_num);

    if ((res = cdev_add(led_cdev, MKDEV(major, minor), DEV_COUNT)))
    {
        err = res;
        goto led_driver_init_ERR3;
    }

    led_cls = class_create(THIS_MODULE, CDEV_CLS_NAME);
    if (IS_ERR(led_cls))
    {
        err = PTR_ERR(led_cls);
        goto led_driver_init_ERR4;
    }

    for (i = 0; i < DEV_COUNT; i++)
    {
        led_device = device_create(led_cls, NULL, MKDEV(major, minor + i), NULL, "%s%d", DEV_NAME, i + 1);
        if (IS_ERR(led_device))
        {
            err = PTR_ERR(led_device);
            goto led_driver_init_ERR5;
        }
    }
    if ((res = led_init()))
    {
        err = res;
        goto led_driver_init_ERR4;
    }
    printk("led_driver_init successful\n");
    return 0;

led_driver_init_ERR5:
    for (i--; i >= 0; i--) // i = 0的时候 i--后 不会执行
    {
        device_destroy(led_cls, MKDEV(major, minor + i));
    }
    class_destroy(led_cls);
led_driver_init_ERR4:
    cdev_del(led_cdev);
led_driver_init_ERR3:
    unregister_chrdev_region(MKDEV(major, minor), DEV_COUNT);
led_driver_init_ERR2:
    kfree(led_cdev);
led_driver_init_ERR1:
    printk("led_driver_init failed\n");
    return err;
}

static void __exit led_driver_exit(void)
{
    int i;
    // if(can_free_led_init)
    // if(can_free_led_driver_init)
    for (i = 0; i < DEV_COUNT; i++)
    {
        gpio_free(gpio_num[i]);
    }
    for (i = 0; i < DEV_COUNT; i++)
    {
        device_destroy(led_cls, MKDEV(major, minor + i));
    }
    class_destroy(led_cls);
    cdev_del(led_cdev);
    unregister_chrdev_region(MKDEV(major, minor), DEV_COUNT);
    kfree(led_cdev);

    printk("led_driver_exit successful\n");
    return;
}

module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("rhf xxx@xxx.com");